System interaction

Environment

R 中词法作用域的机理通过环境实现。

函数优先在内部环境搜索用到的变量,搜索不到时,就到上一级环境搜索。

每加载一个扩展包,这个包的环境都会插入搜索路径,并位于全局环境之前。

环境交互函数
ls()/objects() 列出当前工作空间的所有对象
ls.str(mode=, pattern=) 显示当前环境的结构。mode 参数过滤对象类型,pattern 参数为正则表达式,过滤对象名
rm() 删除一个或更多个对象。
rm(list=ls(all=TRUE)) 删除所有对象
getOption() 查看全局选项
options() 设定全局选项。如 digits=7, 设定显示数字的位数。warn=0, 可以改为1,在警告产生时立即显示。

Path

Working Directory
dir.create() 创建一个目录
getwd()/setwd() 查看/设定当前工作目录

Memory

tracemem()查看对象的内存地址

Time

Sys.sleep() 使程序暂停若干秒,一般用于某些循环(爬虫、动画等),故意降低运行频率

sys.time() 读取系统时间

t1 <- Sys.time()
...
t2 <- Sys.time()
print(t2 - t1) # 中间代码的运行时间

Help

Help
help(functionName)?functionName 查看一个函数的用法
example(functionName) 运行帮主文件中的例子
help.search(keyword)??keyword 在==所有包==的文档中搜索关键词
help(package = "") 查看一个包的帮助
help(options) 显示可用选项的说明
options() 显示或设置当前选项

History

History
history(#) 显示最近使用过的#个命令(默认值为25)
savehistory("myfile") 保存命令历史到文件myfile中(默认值为.Rhistory)
loadhistory("myfile") 载入一个命令历史文件(默认值为.Rhistory)

Modularization of R Coding

R Package

Package
install.packages("package") 安装包
installed.packages() 返回一个数据框,内含所有已安装的包的多项信息
library() 无参数时,显示库中已经安装了哪些包
library(package) 有参数时,载入包
unloadNamespace(package) 解除载入一个包
search() 查看环境中已经加载了哪些包
getOption('defaultPackages') 查看默认加载的 R 包
update.packages("package") 升级包。若不填入参数,则自动升级所有可以升级的包
package::function() 调用相应包的函数(有时多个包用同一个名字命名不同的函数,会发生冲突,只能这样引用
data() 查看所有预先提供的数据
data(package="") 查看某个包所有预先提供的数据
data(dataset_name, package=) 读入包中数据
# 查看 R 包的下载次数
library(cranlogs)

fashion <- function(package_name) {
  d <- cran_downloads(
    package = package_name,
    from = "2020-01-01",
    to = "2021-05-31"
  )
  sum(d$count)
}

fashion('ggplot2')
fashion("tidyverse")
fashion("data.table")
fashion("plotly")

R Script

语法

source('xxx.R')

最佳实践

由于 R 的懒加载特性,模块中的代码不会运行,故 .R 脚本文件作为模块时,不必加载配置常量和包,纯写函数即可,所有的包和配置由主文件加载。

Code Style

格式化

styler 包

styler - A non-invasive source code formatter for R (lorenzwalthert.github.io)

install.packages("styler")
  • style_text() styles a string
  • style_file() styles R and Rmd files
  • style_dir() styles all R and/or Rmd files in a directory.
  • 最常用
    • RStudio Addins 菜单,styles the active file R or Rmd file, or the highlighted code.
    • 或在 console 中输入styler:::style_active_file()

Google R Style

一般性规则

  • 尽量避免使用attach(), detach()
  • Error 应该使用Stop()来抛出
  • S3 类和 S4 类的函数不要一起使用

命名

对象命名以句号.分隔,不用下划线

函数名首字母大写,驼峰式命名法,不用句号分隔

注释

注释行以#开头,后加一个空格

代码行内短注释需要在代码后面空两格,然后#,再加一个空格

对变量和函数的说明写在它们的上方(紧邻),VSCode 的 R 插件能够自动识别

总体布局与顺序

  1. 版权声明
  2. 作者信息
  3. 文件说明, 包括程序的目的,输入以及输出
  4. source() 和 library() 说明
  5. 函数定义
  6. 可执行语句, 如果有的话 (例如, print, plot)

单元测试应在另一个独立的的文件中进行

IDE

Rstudio

R Studio cheatsheet 预览:

Shortcuts
Ctrl + Shift + A 选中部分行后,格式化代码
Ctrl + Alt + I Insert chunk
Alt + - 插入 <-
Ctrl + Shift + M 插入%>%
Alt + Shift + K 显示快捷键
Ctrl + Shift + N 新建脚本 .r文件
Ctrl + Enter
Ctrl + Shift + Enter
Ctrl + Alt + R
运行一行代码
运行代码块
运行全部代码
Shift + Home/End 选中光标到行首/末之间的部分
Tab / Ctrl+Space 自动补齐
输入完函数名,按tab,自动添加开括号(和闭括号)。
Ctrl + Shift + C 注释/取消注释
F1 查看帮助
Ctrl+ ↑ 在 Console 中输入“xxx”,然后按 Ctrl+ ↑。就可以列出所有输入过的以“xxx”开头的命令。

VSCode

优点

  • 鼠标悬停,即可显示变量的定义信息和函数的帮助文档(仅限 R 包中函数的官方文档和本文件中自定义函数的定义,无法显示引入模块中的自定义函数的定义),省去了查阅文档的大量时间
  • 保存(Ctrl+S)时自动格式化

配置步骤

  1. 安装 R 包 languageserver

    install.packages("languageserver")
  2. 在 VSCode 扩展商店中安装 R 插件。

    1. 安装完成后在 VSCode 设置中搜索r.rterm.option,删除--no-save,--no-restore,添加--no-site-file和 R.exe 的路径--r-binary=C:\Program Files\R\R-4.1.3\bin\R.exe
  3. 安装 Radian:一款现代的 R console,它是用 Python 编写的

    pip install radian
    1. 安装完成后在 cmd 中输入 radian 查看是否安装成功。

    2. 若出现 “cannot determine R HOME”,可能存在多个R路径(如新安装了4.1.2版本),而 Radian 无法识别。解决这个问题的方法仍然是在 VSCode 中设置 R.exe 的路径。除了搜索r.rterm.option进行修改,也可以在设置界面的右上角打开 setting.json文件直接修改:

        "r.rterm.option": [
          "--no-site-file",
          "--r-binary=C:\\Program Files\\R\\R-4.1.3\\bin\\R.exe"
        ],
  4. VSCode 中 Radian 相关设置

    1. 搜索 r.rterm.windows,将其设置为 radian.exe 的路径。在 cmd 中(powershell 不行)输入 where radian 可以获取其路径。
    2. 搜索R: Bracketed Paste并勾选,否则 Radian 不会启用
    3. 搜索r.sessionWatcher并勾选

如何在 VSCODE 中高效使用 R 语言 (图文详解)_Baimoc-CSDN博客

LS0tDQp0aXRsZTogIlIgRW5naW5lZXJpbmciDQpzdWJ0aXRsZTogJ1N5c3RlbSwgTW9kdWxhcml6YXRpb24sIENvZGUgU3R5bGUgYW5kIElERScNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCnNvdXJjZSgiLi4vUm1hcmtkb3duLXRlbXBsYXRlL1JtYXJrZG93bl9jb25maWcuUiIpDQoNCiMjIGdsb2JhbCBvcHRpb25zID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIHdpZHRoID0gY29uZmlnJHdpZHRoLA0KICBmaWcud2lkdGggPSBjb25maWckZmlnLndpZHRoLA0KICBmaWcuYXNwID0gY29uZmlnJGZpZy5hc3AsDQogIG91dC53aWR0aCA9IGNvbmZpZyRvdXQud2lkdGgsDQogIGZpZy5hbGlnbiA9IGNvbmZpZyRmaWcuYWxpZ24sDQogIGZpZy5wYXRoID0gY29uZmlnJGZpZy5wYXRoLA0KICBmaWcuc2hvdyA9IGNvbmZpZyRmaWcuc2hvdywNCiAgd2FybiA9IGNvbmZpZyR3YXJuLA0KICB3YXJuaW5nID0gY29uZmlnJHdhcm5pbmcsDQogIG1lc3NhZ2UgPSBjb25maWckbWVzc2FnZSwNCiAgZWNobyA9IGNvbmZpZyRlY2hvLA0KICBldmFsID0gY29uZmlnJGV2YWwsDQogIHRpZHkgPSBjb25maWckdGlkeSwNCiAgY29tbWVudCA9IGNvbmZpZyRjb21tZW50LA0KICBjb2xsYXBzZSA9IGNvbmZpZyRjb2xsYXBzZSwNCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCmBgYA0KDQojIyBTeXN0ZW0gaW50ZXJhY3Rpb24NCg0KIyMjIEVudmlyb25tZW50DQoNClIg5Lit6K+N5rOV5L2c55So5Z+f55qE5py655CG6YCa6L+H546v5aKD5a6e546w44CCDQoNCuWHveaVsOS8mOWFiOWcqOWGhemDqOeOr+Wig+aQnOe0oueUqOWIsOeahOWPmOmHj++8jOaQnOe0ouS4jeWIsOaXtu+8jOWwseWIsOS4iuS4gOe6p+eOr+Wig+aQnOe0ouOAgg0KDQrmr4/liqDovb3kuIDkuKrmianlsZXljIXvvIzov5nkuKrljIXnmoTnjq/looPpg73kvJrmj5LlhaXmkJzntKLot6/lvoTvvIzlubbkvY3kuo7lhajlsYDnjq/looPkuYvliY3jgIINCg0KfCDnjq/looPkuqTkupLlh73mlbAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IGBscygpYC9gb2JqZWN0cygpYCAgICAgICAgfCDliJflh7rlvZPliY3lt6XkvZznqbrpl7TnmoTmiYDmnInlr7nosaEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGxzLnN0cihtb2RlPSwgcGF0dGVybj0pYCB8IOaYvuekuuW9k+WJjeeOr+Wig+eahOe7k+aehOOAgm1vZGUg5Y+C5pWw6L+H5ruk5a+56LGh57G75Z6L77yMcGF0dGVybiDlj4LmlbDkuLrmraPliJnooajovr7lvI/vvIzov4fmu6Tlr7nosaHlkI0gfA0KfCBgcm0oKWAgICAgICAgICAgICAgICAgICAgIHwg5Yig6Zmk5LiA5Liq5oiW5pu05aSa5Liq5a+56LGh44CCICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgcm0obGlzdD1scyhhbGw9VFJVRSkpYCAgIHwg5Yig6Zmk5omA5pyJ5a+56LGhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGdldE9wdGlvbigpYCAgICAgICAgICAgICB8IOafpeeci+WFqOWxgOmAiemhuSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBvcHRpb25zKClgICAgICAgICAgICAgICAgfCDorr7lrprlhajlsYDpgInpobnjgILlpoIgZGlnaXRzPTcsIOiuvuWumuaYvuekuuaVsOWtl+eahOS9jeaVsOOAgndhcm49MCwg5Y+v5Lul5pS55Li6Me+8jOWcqOitpuWRiuS6p+eUn+aXtueri+WNs+aYvuekuuOAgiB8DQoNCiMjIyBQYXRoDQoNCnwgV29ya2luZyAgRGlyZWN0b3J5ICB8ICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0gfA0KfCBgZGlyLmNyZWF0ZSgpYCAgICAgIHwg5Yib5bu65LiA5Liq55uu5b2VICAgICAgICAgIHwNCnwgYGdldHdkKClgL2BzZXR3ZCgpYCB8IOafpeeciy/orr7lrprlvZPliY3lt6XkvZznm67lvZUgfA0KDQoNCg0KIyMjIE1lbW9yeQ0KDQpgdHJhY2VtZW0oKWDmn6XnnIvlr7nosaHnmoTlhoXlrZjlnLDlnYANCg0KIyMjIFRpbWUNCg0KYFN5cy5zbGVlcCgpYCDkvb/nqIvluo/mmoLlgZzoi6XlubLnp5LvvIzkuIDoiKznlKjkuo7mn5Dkupvlvqrnjq/vvIjniKzomavjgIHliqjnlLvnrYnvvInvvIzmlYXmhI/pmY3kvY7ov5DooYzpopHnjocNCg0KYHN5cy50aW1lKClgIOivu+WPluezu+e7n+aXtumXtA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnQxIDwtIFN5cy50aW1lKCkNCi4uLg0KdDIgPC0gU3lzLnRpbWUoKQ0KcHJpbnQodDIgLSB0MSkgIyDkuK3pl7Tku6PnoIHnmoTov5DooYzml7bpl7QNCmBgYA0KDQoNCiMjIyBIZWxwDQoNCnwgSGVscCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfA0KfCBgaGVscChmdW5jdGlvbk5hbWUpYOaIlmA/ZnVuY3Rpb25OYW1lYCB8IOafpeeci+S4gOS4quWHveaVsOeahOeUqOazlSAgICAgICAgICAgICB8DQp8IGBleGFtcGxlKGZ1bmN0aW9uTmFtZSlgICAgICAgICAgICAgICAgfCDov5DooYzluK7kuLvmlofku7bkuK3nmoTkvovlrZAgICAgICAgICAgIHwNCnwgYGhlbHAuc2VhcmNoKGtleXdvcmQpYOaIlmA/P2tleXdvcmRgICAgfCDlnKg9PeaJgOacieWMhT0955qE5paH5qGj5Lit5pCc57Si5YWz6ZSu6K+NIHwNCnwgYGhlbHAocGFja2FnZSA9ICIiKWAgICAgICAgICAgICAgICAgICB8IOafpeeci+S4gOS4quWMheeahOW4ruWKqSAgICAgICAgICAgICAgIHwNCnwgYGhlbHAob3B0aW9ucylgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuWPr+eUqOmAiemhueeahOivtOaYjiAgICAgICAgICAgICB8DQp8IGBvcHRpb25zKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmmL7npLrmiJborr7nva7lvZPliY3pgInpobkgICAgICAgICAgICAgfA0KDQojIyMgSGlzdG9yeQ0KDQp8IEhpc3RvcnkgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGhpc3RvcnkoIylgICAgICAgICAgICAgfCDmmL7npLrmnIDov5Hkvb/nlKjov4fnmoQj5Liq5ZG95Luk77yI6buY6K6k5YC85Li6MjXvvIkgICAgICAgICAgIHwNCnwgYHNhdmVoaXN0b3J5KCJteWZpbGUiKWAgfCDkv53lrZjlkb3ku6Tljoblj7LliLDmlofku7ZteWZpbGXkuK3vvIjpu5jorqTlgLzkuLouUmhpc3RvcnnvvIkgfA0KfCBgbG9hZGhpc3RvcnkoIm15ZmlsZSIpYCB8IOi9veWFpeS4gOS4quWRveS7pOWOhuWPsuaWh+S7tu+8iOm7mOiupOWAvOS4ui5SaGlzdG9yee+8iSAgICAgICB8DQoNCg0KDQojIyBNb2R1bGFyaXphdGlvbiBvZiBSIENvZGluZw0KDQojIyMgUiBQYWNrYWdlDQoNCnwgUGFja2FnZSAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGluc3RhbGwucGFja2FnZXMoInBhY2thZ2UiKWAgIHwg5a6J6KOF5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGluc3RhbGxlZC5wYWNrYWdlcygpYCAgICAgICAgIHwg6L+U5Zue5LiA5Liq5pWw5o2u5qGG77yM5YaF5ZCr5omA5pyJ5bey5a6J6KOF55qE5YyF55qE5aSa6aG55L+h5oGvICAgICAgICAgICAgICAgICB8DQp8IGBsaWJyYXJ5KClgICAgICAgICAgICAgICAgICAgICB8IOaXoOWPguaVsOaXtu+8jOaYvuekuuW6k+S4reW3sue7j+WuieijheS6huWTquS6m+WMhSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGxpYnJhcnkocGFja2FnZSlgICAgICAgICAgICAgIHwg5pyJ5Y+C5pWw5pe277yM6L295YWl5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgdW5sb2FkTmFtZXNwYWNlKHBhY2thZ2UpYCAgICAgfCDop6PpmaTovb3lhaXkuIDkuKrljIUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHNlYXJjaCgpYCAgICAgICAgICAgICAgICAgICAgIHwg5p+l55yL546v5aKD5Lit5bey57uP5Yqg6L295LqG5ZOq5Lqb5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBnZXRPcHRpb24oJ2RlZmF1bHRQYWNrYWdlcycpYCB8IOafpeeci+m7mOiupOWKoOi9veeahCBSIOWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHVwZGF0ZS5wYWNrYWdlcygicGFja2FnZSIpYCAgIHwg5Y2H57qn5YyF44CC6Iul5LiN5aGr5YWl5Y+C5pWw77yM5YiZ6Ieq5Yqo5Y2H57qn5omA5pyJ5Y+v5Lul5Y2H57qn55qE5YyFICAgICAgICAgICAgIHwNCnwgYHBhY2thZ2U6OmZ1bmN0aW9uKClgICAgICAgICAgIHwg6LCD55So55u45bqU5YyF55qE5Ye95pWw77yI5pyJ5pe2KirlpJrkuKrljIXnlKjlkIzkuIDkuKrlkI3lrZflkb3lkI3kuI3lkIznmoTlh73mlbDvvIzkvJrlj5HnlJ/lhrLnqoHvvIzlj6rog73ov5nmoLflvJXnlKgqKu+8iSB8DQp8IGBkYXRhKClgICAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+aJgOaciemihOWFiOaPkOS+m+eahOaVsOaNriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGRhdGEocGFja2FnZT0iIilgICAgICAgICAgICAgIHwg5p+l55yL5p+Q5Liq5YyF5omA5pyJ6aKE5YWI5o+Q5L6b55qE5pWw5o2uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgZGF0YShkYXRhc2V0X25hbWUsIHBhY2thZ2U9KWAgfCDor7vlhaXljIXkuK3mlbDmja4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpgYGBSDQojIOafpeeciyBSIOWMheeahOS4i+i9veasoeaVsA0KbGlicmFyeShjcmFubG9ncykNCg0KZmFzaGlvbiA8LSBmdW5jdGlvbihwYWNrYWdlX25hbWUpIHsNCiAgZCA8LSBjcmFuX2Rvd25sb2FkcygNCiAgICBwYWNrYWdlID0gcGFja2FnZV9uYW1lLA0KICAgIGZyb20gPSAiMjAyMC0wMS0wMSIsDQogICAgdG8gPSAiMjAyMS0wNS0zMSINCiAgKQ0KICBzdW0oZCRjb3VudCkNCn0NCg0KZmFzaGlvbignZ2dwbG90MicpDQpmYXNoaW9uKCJ0aWR5dmVyc2UiKQ0KZmFzaGlvbigiZGF0YS50YWJsZSIpDQpmYXNoaW9uKCJwbG90bHkiKQ0KYGBgDQoNCiMjIyBSIFNjcmlwdA0KDQojIyMjIOivreazlQ0KDQpgc291cmNlKCd4eHguUicpYCANCg0KIyMjIyDmnIDkvbPlrp7ot7UNCg0K55Sx5LqOIFIg55qE5oeS5Yqg6L2954m55oCn77yM5qih5Z2X5Lit55qE5Luj56CB5LiN5Lya6L+Q6KGM77yM5pWFIC5SIOiEmuacrOaWh+S7tuS9nOS4uuaooeWdl+aXtu+8jOS4jeW/heWKoOi9vemFjee9ruW4uOmHj+WSjOWMhe+8jOe6r+WGmeWHveaVsOWNs+WPr++8jOaJgOacieeahOWMheWSjOmFjee9rueUseS4u+aWh+S7tuWKoOi9veOAgg0KDQoNCg0KIyMgQ29kZSBTdHlsZQ0KDQojIyMg5qC85byP5YyWDQoNCiMjIyMgc3R5bGVyIOWMhQ0KDQpbc3R5bGVyIC0gQSBub24taW52YXNpdmUgc291cmNlIGNvZGUgZm9ybWF0dGVyIGZvciBSIChsb3Jlbnp3YWx0aGVydC5naXRodWIuaW8pXShodHRwczovL2xvcmVuendhbHRoZXJ0LmdpdGh1Yi5pby9zdHlsZXJwb3N0LykNCg0KYGBgUg0KaW5zdGFsbC5wYWNrYWdlcygic3R5bGVyIikNCmBgYA0KDQotIGBzdHlsZV90ZXh0KClgIHN0eWxlcyBhIHN0cmluZw0KLSBgc3R5bGVfZmlsZSgpYCBzdHlsZXMgUiBhbmQgUm1kIGZpbGVzDQotIGBzdHlsZV9kaXIoKWAgc3R5bGVzIGFsbCBSIGFuZC9vciBSbWQgZmlsZXMgaW4gYSBkaXJlY3RvcnkuDQotICoq5pyA5bi455SoKioNCiAgLSBSU3R1ZGlvIGBBZGRpbnNgIOiPnOWNle+8jHN0eWxlcyB0aGUgYWN0aXZlIGZpbGUgUiBvciBSbWQgZmlsZSwgb3IgdGhlIGhpZ2hsaWdodGVkIGNvZGUuDQogIC0g5oiW5ZyoIGNvbnNvbGUg5Lit6L6T5YWlYHN0eWxlcjo6OnN0eWxlX2FjdGl2ZV9maWxlKClgDQogIA0KDQojIyMgR29vZ2xlIFIgU3R5bGUNCg0KIyMjIyDkuIDoiKzmgKfop4TliJkNCg0KLSDlsL3ph4/pgb/lhY3kvb/nlKhgYXR0YWNoKClgLCBgZGV0YWNoKClgDQotIEVycm9yIOW6lOivpeS9v+eUqGBTdG9wKClg5p2l5oqb5Ye6DQotIFMzIOexu+WSjCBTNCDnsbvnmoTlh73mlbDkuI3opoHkuIDotbfkvb/nlKgNCg0KIyMjIyDlkb3lkI0NCg0K5a+56LGh5ZG95ZCN5Lul5Y+l5Y+3YC5g5YiG6ZqU77yM5LiN55So5LiL5YiS57q/DQoNCuWHveaVsOWQjemmluWtl+avjeWkp+WGme+8jOmpvOWzsOW8j+WRveWQjeazle+8jOS4jeeUqOWPpeWPt+WIhumalA0KDQojIyMjIOazqOmHig0KDQrms6jph4rooYzku6VgI2DlvIDlpLTvvIzlkI7liqDkuIDkuKrnqbrmoLwNCg0K5Luj56CB6KGM5YaF55+t5rOo6YeK6ZyA6KaB5Zyo5Luj56CB5ZCO6Z2i56m65Lik5qC877yM54S25ZCOYCNg77yM5YaN5Yqg5LiA5Liq56m65qC8DQoNCioq5a+55Y+Y6YeP5ZKM5Ye95pWw55qE6K+05piO5YaZ5Zyo5a6D5Lus55qE5LiK5pa577yI57Sn6YK777yJ77yMVlNDb2RlIOeahCBSIOaPkuS7tuiDveWkn+iHquWKqOivhuWIqyoqDQoNCiMjIyMg5oC75L2T5biD5bGA5LiO6aG65bqPDQoNCjEuIOeJiOadg+WjsOaYjg0KMi4g5L2c6ICF5L+h5oGvDQozLiDmlofku7bor7TmmI4sIOWMheaLrOeoi+W6j+eahOebrueahO+8jOi+k+WFpeS7peWPiui+k+WHug0KNC4gc291cmNlKCkg5ZKMIGxpYnJhcnkoKSDor7TmmI4NCjUuIOWHveaVsOWumuS5iQ0KNi4g5Y+v5omn6KGM6K+t5Y+lLCDlpoLmnpzmnInnmoTor50gKOS+i+WmgiwgcHJpbnQsIHBsb3QpDQoNCuWNleWFg+a1i+ivleW6lOWcqOWPpuS4gOS4queLrOeri+eahOeahOaWh+S7tuS4rei/m+ihjA0KDQoNCg0KIyMgSURFDQoNCiMjIyBSc3R1ZGlvDQoNCltSIFN0dWRpbyBjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL3JzdHVkaW8taWRlLnBkZikg6aKE6KeI77yaDQoNCjxvYmplY3QgZGF0YT0iLi4vcGRmL2NoZWF0c2hlZXQtcnN0dWRpby1pZGUucGRmIiB0eXBlPSJhcHBsaWNhdGlvbi9wZGYiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjwvb2JqZWN0Pg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmRvd25sb2FkdGhpczo6ZG93bmxvYWRfZmlsZSgNCiAgcGF0aCA9ICIuLi9wZGYvY2hlYXRzaGVldC1yc3R1ZGlvLWlkZS5wZGYiLA0KICBvdXRwdXRfbmFtZSA9ICJjaGVhdHNoZWV0LXJzdHVkaW8taWRlIiwNCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGNoZWF0c2hlZXQiLA0KICBidXR0b25fdHlwZSA9ICJzdWNjZXNzIiwNCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQ0KKQ0KYGBgDQoNCg0KfCBTaG9ydGN1dHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IEN0cmwgKyBTaGlmdCArIEEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3pg6jliIbooYzlkI7vvIzmoLzlvI/ljJbku6PnoIEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBDdHJsICsgQWx0ICsgSSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgSW5zZXJ0IGNodW5rICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSA8LSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgU2hpZnQgKyBLICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuW/q+aNt+mUriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaWsOW7uuiEmuacrCAucuaWh+S7tiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEN0cmwgKyBFbnRlcjxiciAvPkN0cmwgKyBTaGlmdCArIEVudGVyPGJyIC8+Q3RybCArIEFsdCArIFIgfCDov5DooYzkuIDooYzku6PnoIE8YnIgLz7ov5DooYzku6PnoIHlnZc8YnIgLz7ov5DooYzlhajpg6jku6PnoIEgICAgICAgICAgICAgICB8DQp8IFNoaWZ0ICsgSG9tZS9FbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3lhYnmoIfliLDooYzpppYv5pyr5LmL6Ze055qE6YOo5YiGICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgVGFiIC8gQ3RybCtTcGFjZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOiHquWKqOihpem9kDxiciAvPui+k+WFpeWujOWHveaVsOWQje+8jOaMiXRhYu+8jOiHquWKqOa3u+WKoOW8gOaLrOWPtyjlkozpl63mi6zlj7cp44CCIHwNCnwgQ3RybCArIFNoaWZ0ICsgQyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOazqOmHii/lj5bmtojms6jph4ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEYxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvluK7liqkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCsg4oaRICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZyoIENvbnNvbGUg5Lit6L6T5YWl4oCceHh44oCd77yM54S25ZCO5oyJIEN0cmwrIOKGkeOAguWwseWPr+S7peWIl+WHuuaJgOaciei+k+WFpei/h+eahOS7peKAnHh4eOKAneW8gOWktOeahOWRveS7pOOAgiB8DQoNCiMjIyBWU0NvZGUNCg0KIyMjIyDkvJjngrkNCg0KLSDpvKDmoIfmgqzlgZzvvIzljbPlj6/mmL7npLrlj5jph4/nmoTlrprkuYnkv6Hmga/lkozlh73mlbDnmoTluK7liqnmlofmoaPvvIjku4XpmZAgUiDljIXkuK3lh73mlbDnmoTlrpjmlrnmlofmoaPlkozmnKzmlofku7bkuK3oh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvIzml6Dms5XmmL7npLrlvJXlhaXmqKHlnZfkuK3nmoToh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvInvvIznnIHljrvkuobmn6XpmIXmlofmoaPnmoTlpKfph4/ml7bpl7QNCi0g5L+d5a2Y77yIQ3RybCtT77yJ5pe26Ieq5Yqo5qC85byP5YyWDQoNCiMjIyMg6YWN572u5q2l6aqkDQoNCjEuIOWuieijhSBSIOWMhSBsYW5ndWFnZXNlcnZlcg0KDQogICBgYGBSDQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYW5ndWFnZXNlcnZlciIpDQogICBgYGANCg0KMi4g5ZyoIFZTQ29kZSDmianlsZXllYblupfkuK3lronoo4UgUiDmj5Lku7bjgIINCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIFZTQ29kZSDorr7nva7kuK3mkJzntKJgci5ydGVybS5vcHRpb25g77yM5Yig6ZmkYC0tbm8tc2F2ZSwtLW5vLXJlc3RvcmVg77yM5re75YqgYC0tbm8tc2l0ZS1maWxlYOWSjCBSLmV4ZSDnmoTot6/lvoRgLS1yLWJpbmFyeT1DOlxQcm9ncmFtIEZpbGVzXFJcUi00LjEuM1xiaW5cUi5leGVgDQoNCjMuIOWuieijhSBSYWRpYW7vvJrkuIDmrL7njrDku6PnmoQgUiBjb25zb2xl77yM5a6D5piv55SoIFB5dGhvbiDnvJblhpnnmoQNCg0KICAgYGBgcG93ZXJzaGVsbA0KICAgcGlwIGluc3RhbGwgcmFkaWFuDQogICBgYGANCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIGNtZCDkuK3ovpPlhaUgYHJhZGlhbmAg5p+l55yL5piv5ZCm5a6J6KOF5oiQ5Yqf44CCDQoNCiAgIDIuIOiLpeWHuueOsCDigJxjYW5ub3QgZGV0ZXJtaW5lIFIgSE9NReKAne+8jOWPr+iDveWtmOWcqOWkmuS4qlLot6/lvoTvvIjlpoLmlrDlronoo4XkuoY0LjEuMueJiOacrO+8ie+8jOiAjCBSYWRpYW4g5peg5rOV6K+G5Yir44CC6Kej5Yaz6L+Z5Liq6Zeu6aKY55qE5pa55rOV5LuN54S25piv5ZyoIFZTQ29kZSDkuK3orr7nva4gUi5leGUg55qE6Lev5b6E44CC6Zmk5LqG5pCc57SiYHIucnRlcm0ub3B0aW9uYOi/m+ihjOS/ruaUue+8jOS5n+WPr+S7peWcqOiuvue9rueVjOmdoueahOWPs+S4iuinkuaJk+W8gCBzZXR0aW5nLmpzb27mlofku7bnm7TmjqXkv67mlLnvvJoNCg0KICAgICAgYGBgYGpzb24NCiAgICAgICAgInIucnRlcm0ub3B0aW9uIjogWw0KICAgICAgICAgICItLW5vLXNpdGUtZmlsZSIsDQogICAgICAgICAgIi0tci1iaW5hcnk9QzpcXFByb2dyYW0gRmlsZXNcXFJcXFItNC4xLjNcXGJpblxcUi5leGUiDQogICAgICAgIF0sDQogICAgICBgYGBgDQoNCjQuIFZTQ29kZSDkuK0gUmFkaWFuIOebuOWFs+iuvue9rg0KDQogICAxLiDmkJzntKIgYHIucnRlcm0ud2luZG93c2DvvIzlsIblhbborr7nva7kuLogcmFkaWFuLmV4ZSDnmoTot6/lvoTjgILlnKggY21kIOS4re+8iHBvd2Vyc2hlbGwg5LiN6KGM77yJ6L6T5YWlIGB3aGVyZSByYWRpYW5gIOWPr+S7peiOt+WPluWFtui3r+W+hOOAgg0KICAgMi4g5pCc57SiYFI6IEJyYWNrZXRlZCBQYXN0ZWDlubbli77pgInvvIzlkKbliJkgUmFkaWFuIOS4jeS8muWQr+eUqA0KICAgMy4g5pCc57SiYHIuc2Vzc2lvbldhdGNoZXJg5bm25Yu+6YCJDQoNCg0KDQpb5aaC5L2V5ZyoIFZTQ09ERSDkuK3pq5jmlYjkvb/nlKggUiDor63oqIAg77yI5Zu+5paH6K+m6Kej77yJX0JhaW1vYy1DU0RO5Y2a5a6iXShodHRwczovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI2MjI1My9hcnRpY2xlL2RldGFpbHMvMTEzODM3NzIwKQ0K